import os
import json
import threading
from datetime import datetime
from .dot_field_object import to_obj, to_value
import time


class DatetimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        return json.JSONEncoder.default(self, obj)


def json_file2obj(file_path, encoding='utf-8'):
    with open(file_path, encoding=encoding) as fp:
        return to_obj(json.load(fp))


def obj2json_file(obj, file_path, encoding='utf-8'):
    obj = to_value(obj)
    with open(file_path, "w", encoding=encoding) as f:
        json.dump(obj, f, cls=DatetimeEncoder)


class JsonListFile:
    def __init__(self, file_path, obj_type: str):
        self._file_path = file_path
        self._type = obj_type
        if not os.path.exists(file_path):
            with open(file_path, "w") as f:
                f.write('[]')
        with open(file_path) as f:
            self._objects = json.load(f)
        for obj in self._objects:
            obj['_type'] = self._type
        self._lock = threading.RLock()

    def get_ids(self):
        with self._lock:
            return [obj['id'] for obj in self._objects]

    def add_object(self, object):
        with self._lock:
            id = object['id']
            if id is None:
                id = len(self._objects) + 1
                object['id'] = id
            assert self.get_object(id) is None, 'id "%s" is duplicated' % id
            now = time.time()
            object['create_time'] = now
            object['last_updated'] = now
            self._objects.append(object)
            self._synchronize()
        return object

    def _synchronize(self):
        obj2json_file(self._objects, self._file_path)

    def get_object(self, id):
        with self._lock:
            for obj in self._objects:
                if obj['id'] == id:
                    return obj
        return None

    def __getitem__(self, item):
        return self.get_object(item)

    def get_objects(self, ids=None, condition: str = None):
        with self._lock:
            if ids is None:
                if condition is None:
                    return self._objects
                return [obj for obj in self._objects if self.check_condition(obj, condition)]
            return [obj for obj in self._objects if obj['id'] in ids and self.check_condition(obj, condition)]

    def check_condition(self, obj, condition):
        var_values = {key: to_obj(obj[key]) for key in obj}
        return eval(condition, var_values)

    def has_id(self, id):
        return self.get_object(id) is not None

    def remove_id(self, id):
        with self._lock:
            for i, obj in enumerate(self._objects):
                if obj['id'] == id:
                    del self._objects[i]
                    self._synchronize()

    def remove_ids(self, ids):
        with self._lock:
            r_ids = []
            for i, obj in enumerate(self._objects):
                if obj['id'] in ids:
                    r_ids.append(i)
            for i in reversed(r_ids):
                del self._objects[i]
            self._synchronize()

    def for_each(self, function):
        with self._lock:
            for obj in self._objects:
                function(obj)

    def select(self, function):
        with self._lock:
            return [obj for obj in self._objects if function(obj)]

    # def update(self, old_obj, new_obj):
    #     id = old_obj['id']
    #     assert id == new_obj['id'], "old obj's id is %d, while new obj's id is %d" % (id, new_obj['id'])
    #
    #     with self._lock:
    #         ori_obj = self.get_object(id)
    #         old_obj['_type'] = self._type
    #         self.check_equals(ori_obj, old_obj)
    #         self.remove_id(id)
    #         new_obj['last_updated'] = time.time()
    #         self.add_object(new_obj)

    def update(self, new_obj):
        with self._lock:
            self.remove_id(id)
            new_obj['last_updated'] = time.time()
            self.add_object(new_obj)

    def check_equals(self, base, obj):
        for key in base:
            if key not in obj or not (base[key] == obj[key] or base[key] is None and obj[key]==''):
                raise AssertionError('对象的%s前后不一致，前：%s, 后：%s' % (key, base[key], obj[key]))
